/****************************************************
	文件：Planner.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/8/26 17:46:39
	功能：规划器（框架层）
*****************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

namespace AIFramework.HTN
{
    /// <summary>
    /// 记录任务分解
    /// </summary>
    public class PlanMemento
    {
        protected List<TaskBase> mTaskToProcess = new List<TaskBase>();
        protected List<PrimitiveTask> mFinalPlan = new List<PrimitiveTask>();
        protected Method mCurMethod;
        protected TaskBase mBelongCompound;
        public TaskBase BelongCompound => mBelongCompound;

        public void Reset()
        {
            mTaskToProcess.Clear();
            mFinalPlan.Clear();
            mCurMethod = null;
            mBelongCompound = null;
        }

        public void Save(Stack<TaskBase> taskToProcess, Queue<PrimitiveTask> finalPlan, Method curMethod, TaskBase belongCompound)
        {
            foreach (var item in taskToProcess)
            {
                mTaskToProcess.Add(item);
            }
            foreach (var item in finalPlan)
            {
                mFinalPlan.Add(item);
            }
            mCurMethod = curMethod;
            mBelongCompound = belongCompound;
        }
        public void Recover(ref Stack<TaskBase> taskToProcess, ref Queue<PrimitiveTask> finalPlan, ref Method curMethod, ref TaskBase belongCompound)
        {
            taskToProcess.Clear();
            finalPlan.Clear();
            for (int i = mTaskToProcess.Count - 1; i >= 0; i++)
            {
                taskToProcess.Push(mTaskToProcess[i]);
            }
            for (int i = 0; i < mFinalPlan.Count; i++)
            {
                finalPlan.Enqueue(mFinalPlan[i]);
            }
            curMethod = mCurMethod;
            belongCompound = mBelongCompound;
        }

    }
    public class Planner
    {
        /// <summary>
        /// 当前的世界状态
        /// </summary>
        protected WorldState mCurWorldState;
        /// <summary>
        /// 工作状态（世界状态的副本）
        /// </summary>
        protected WorldState mWorkingWorldState;
        /// <summary>
        /// 规划域
        /// </summary>
        protected DomainBase mDomain;
        /// <summary>
        /// 任务集合副本
        /// </summary>
        protected List<TaskBase> mTaskLst = new List<TaskBase>();
        /// <summary>
        /// 待处理的任务
        /// </summary>
        protected Stack<TaskBase> mTaskToProcess = new Stack<TaskBase>();
        /// <summary>
        /// 最终任务队列（任务行动队列）
        /// </summary>
        protected Queue<PrimitiveTask> mFinalPlan = new Queue<PrimitiveTask>();
        /// <summary>
        /// 当前任务
        /// </summary>
        protected TaskBase mCurTask;

        public void Init(WorldState ws, DomainBase domain)
        {
            mCurWorldState = ws;
            mDomain = domain;
            mWorkingWorldState = new WorldState(ws.Self);
            mWorkingWorldState.CopyFrom(mCurWorldState);
            CopyDomain(mDomain);
        }
        public void Reset()
        {
            mCurWorldState.Reset();
            mWorkingWorldState.Reset();
            mTaskLst.Clear();
            mTaskToProcess.Clear();
            mDomain = null;
        }

        public void BulidPlan()
        {
            mWorkingWorldState.CopyFrom(mCurWorldState);
            Method validMethod = null;
            CompoundTask cTask = null;
            PrimitiveTask pTask = null;
            PlanMemento memento = new PlanMemento();
            mTaskToProcess.Push(mTaskLst[0]);
            while (mTaskToProcess.Count > 0) //深搜
            {
                LogTasks(mTaskToProcess, "TaskToProcess");
                mCurTask = mTaskToProcess.Pop();
                Debug.LogFormat("弹出栈顶{0}：{1}", mCurTask.TaskType.ToString(), mCurTask.Name);
                if (mCurTask.TaskType == TaskType.Compound) //复合任务
                {
                    cTask = mCurTask as CompoundTask;
                    validMethod = cTask.FindValidMethod(mWorkingWorldState);
                    Debug.LogFormat("尝试从{0}--{1}中搜索合法Method", cTask.TaskType.ToString(), cTask.Name);
                    if (validMethod != null)
                    {
                        Debug.LogFormat("搜索合法Method：{0}", validMethod.Id);
                        Debug.Log("记录复合任务分解过程");
                        //记录复合任务分解过程
                        memento.Save(mTaskToProcess, mFinalPlan, validMethod, cTask);
                        Debug.Log("将任务集合Push到 待处理的任务 栈顶");
                        InsertTop(validMethod.SubTasks);
                    }
                    else
                    {
                        Debug.LogFormat("复合任务 {0} 回溯为 {1}", mCurTask.Name, memento.BelongCompound.Name);
                        //回溯
                        memento.Recover(ref mTaskToProcess, ref mFinalPlan, ref validMethod, ref mCurTask);
                    }
                }
                else //基元任务
                {
                    pTask = mCurTask as PrimitiveTask;
                    Debug.LogFormat("尝试检验 基元任务 {0} 的前置条件", pTask.Name);
                    if (pTask.CheckCond(mWorkingWorldState))
                    {
                        Debug.LogFormat("通过 基元任务 {0} 的前置条件，ApplyEffects", pTask.Name);
                        pTask.ApplyEffects(mWorkingWorldState);
                        pTask.ApplyExpectedEffects(mWorkingWorldState);
                        Debug.LogFormat("将 {0} 压入最终计划", pTask.Name);
                        mFinalPlan.Enqueue(pTask);
                    }
                    else
                    {
                        Debug.LogFormat("基元任务 {0} 回溯为 {1}", mCurTask.Name, memento.BelongCompound.Name);
                        //回溯
                        memento.Recover(ref mTaskToProcess, ref mFinalPlan, ref validMethod, ref mCurTask);
                    }
                }
            }
            LogTasks(mFinalPlan, "FinalPlan");
        }
        /// <summary>
        /// 获取最终计划
        /// </summary>
        /// <returns></returns>
        public Queue<PrimitiveTask> GetFinalTasks()
        {
            return mFinalPlan;
        }
        /// <summary>
        /// 更新 规划器 当前世界状态
        /// </summary>
        /// <param name="ws">当前世界状态</param>
        public void UpdateCurWorldState(WorldState ws)
        {
            mCurWorldState = ws;
            mWorkingWorldState.CopyFrom(mCurWorldState);
        }
        /// <summary>
        /// 复制 规划域 的任务集合
        /// </summary>
        /// <param name="domain">规划域</param>
        public void CopyDomain(DomainBase domain)
        {
            mTaskLst.Clear();
            for (int i = 0; i < domain.TaskLst.Count; i++)
            {
                mTaskLst.Add(domain.TaskLst[i]);
            }
        }
        /// <summary>
        /// 将任务集合Push到 待处理的任务 栈顶
        /// </summary>
        /// <param name="tasks"></param>
        public void InsertTop(List<TaskBase> tasks)
        {
            for (int i = tasks.Count - 1; i >= 0; i--)
            {
                mTaskToProcess.Push(tasks[i]);
            }
        }

        /// <summary>
        /// 打印任务表（测试使用）
        /// </summary>
        public void LogTasks(IEnumerable<TaskBase> tasks, string title)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var task in tasks)
            {
                sb.Append("//任务类型：").Append(task.TaskType.ToString()).Append("--任务名称：").Append(task.Name).Append("//\n");
            }
            Debug.LogWarning(title + "  LogTasks：\n" + sb.ToString());
        }
    }
}
